package amd64

import (
	"fmt"

	"gitee.com/u-language/u-language/ucom/ast"
	"gitee.com/u-language/u-language/ucom/ir"
)

// MovIrToAsm 将mov ir转换为asm
func MovIrToAsm(to *AstToAsm, irnode ir.IrNode, sbt *ast.Sbt, vreg *VReg, stack *Stack, ret *[]TextNode) {
	switch irnode.ResultTyp {
	case ir.Var:
		(*ret) = append((*ret), SetVar(to, irnode.ResultObj, irnode.Arg1Obj, sbt, vreg))
	case ir.StackVar:
		(*ret) = append((*ret), StackSetVar(to, stack.HaveVar(irnode.ResultObj).offset, irnode.ResultObj, irnode.Arg1Obj, sbt, vreg))
	default:
		panic(fmt.Errorf("未知的赋值目的操作数类型：%s", irnode.String()))
	}
}

// Op2IrToAsm 将一个源操作数和目的操作数一样 的ir转换为asm
func Op2IrToAsm(to *AstToAsm, op ir.OPEnum, dest, src string, destTyp, srcTyp ir.ArgEnum, tmpreg *RegEnum, sbt *ast.Sbt, vreg *VReg, stack *Stack, ret *[]TextNode, i int) {
	switch srcTyp {
	case ir.ImmInt:
		switch destTyp {
		case ir.Var: //op<var,immint,var>或op<immint,var,var> i==0 ok
			(*ret) = append((*ret), NewSrcNumDestMem(dest, src, opisfloat(op, false)))
		case ir.StackVar: //op<stackvar,immint,stackvar>或op<immint,stackvar,stackvar> i==0 ok
			(*ret) = append((*ret), NewSrcNumDestRegOffset(RBP, src, -stack.HaveVar(dest).offset, opisfloat(op, false)))
		default:
			panic(fmt.Errorf("未知的目的操作数类型：name=%s typ=%s", dest, destTyp.String()))
		}
	case ir.ImmFloat:
		switch destTyp {
		case ir.Var:
			data := NewAutoData(src, 4)
			to.Datas.Add(data)
			xmm1 := vreg.GetSSEReg()
			(*ret) = append((*ret), NewSrcMemDestReg(xmm1, data.Name, MOVSS))
			(*ret) = append((*ret), NewSrcRegDestMem(dest, xmm1, opisfloat(op, true)))
		case ir.StackVar:
			data := NewAutoData(src, 4)
			to.Datas.Add(data)
			xmm1 := vreg.GetSSEReg()
			(*ret) = append((*ret), NewSrcMemDestReg(xmm1, data.Name, MOVSS))
			(*ret) = append((*ret), NewSrcRegDestRegOffset(RBP, xmm1, -stack.HaveVar(dest).offset, opisfloat(op, true)))
		default:
			panic(fmt.Errorf("未知的目的操作数类型：name=%s typ=%s", dest, destTyp.String()))
		}
	case ir.Tmp:
		f := isfloatir(dest, tmpreg, sbt, vreg)
		switch destTyp {
		case ir.Var: //op<tmp,var,var> 或op<var,tmp,tmp> i!=0
			(*ret) = append((*ret), NewSrcRegDestMem(dest, *tmpreg, opisfloat(op, f)))
		case ir.StackVar: //op<stacktmp,var,var> 或op<stackvar,tmp,tmp> i!=0
			(*ret) = append((*ret), NewSrcRegDestRegOffset(RBP, *tmpreg, -stack.HaveVar(dest).offset, opisfloat(op, f)))
		default:
			panic(fmt.Errorf("未知的目的操作数类型：name=%s typ=%s", dest, destTyp.String()))
		}
	case ir.Var:
		f := isfloatir(dest, tmpreg, sbt, vreg)
		switch destTyp {
		case ir.Var: //op<var1,var2,var1>或op<var2,var1,var1> 分配可能使用过的寄存器
			tmp := retvreg(src, sbt, vreg)
			(*ret) = append((*ret), NewSrcMemDestReg(tmp, src, opisfloat(ir.MOVOP, f)))
			(*ret) = append((*ret), NewSrcRegDestMem(dest, tmp, opisfloat(op, f)))
		case ir.StackVar: //op<stackvar,var,stackvar> 或op <var,stackvar,stackvar> 分配可能使用过的寄存器
			tmp := retvreg(src, sbt, vreg)
			(*ret) = append((*ret), NewSrcMemDestReg(tmp, src, opisfloat(ir.MOVOP, f)))
			(*ret) = append((*ret), NewSrcRegDestRegOffset(RBP, tmp, -stack.HaveVar(dest).offset, opisfloat(op, f)))
		default:
			panic(fmt.Errorf("未知的目的操作数类型：name=%s typ=%s", dest, destTyp.String()))
		}
	default:
		panic(fmt.Errorf("未知的源操作数类型：name=%s typ=%s", src, srcTyp.String()))
	}
}

// Op2IrAutoToAsm判断ir是不是形如 op<dest,src,dest> 或op<src,dest,dest>的ir，如果是,转换为asm返回true,否则返回false
func Op2IrAutoToAsm(to *AstToAsm, irnode ir.IrNode, tmpreg *RegEnum, sbt *ast.Sbt, vreg *VReg, stack *Stack, ret *[]TextNode, i int) bool {
	if irnode.Arg1Obj == irnode.ResultObj {
		Op2IrToAsm(to, irnode.Op, irnode.ResultObj, irnode.Arg2Obj, irnode.ResultTyp, irnode.Arg2Typ, tmpreg, sbt, vreg, stack, ret, i)
		return true
	}
	if irnode.Arg2Obj == irnode.ResultObj {
		Op2IrToAsm(to, irnode.Op, irnode.ResultObj, irnode.Arg1Obj, irnode.ResultTyp, irnode.Arg1Typ, tmpreg, sbt, vreg, stack, ret, i)
		return true
	}
	return false
}

// OpVarAndImmIntDestTmpIrToAsm将形如 op<var,immint,tmp>或op<immint,var,tmp>的ir转换为asm
func OpVarAndImmIntDestTmpIrToAsm(varname, immint string, tmpreg *RegEnum, op ir.OPEnum, vreg *VReg, ret *[]TextNode, i int) {
	if *tmpreg == 0 {
		*tmpreg = vreg.GetX86Reg(vrm64bit)
	}
	(*ret) = append((*ret), NewSrcMemDestReg(*tmpreg, varname, opisfloat(movOrOp(i, op), false)))
	(*ret) = append((*ret), NewSrcNumDestReg(*tmpreg, immint, opisfloat(op, false)))
}

// OpVarAndImmFloatDestTmpIrToAsm将形如 op<var,immfloat,tmp>或op<immfloat,var,tmp>的ir转换为asm
func OpVarAndImmFloatDestTmpIrToAsm(to *AstToAsm, varname, immfloat string, tmpreg *RegEnum, op ir.OPEnum, vreg *VReg, ret *[]TextNode, i int) {
	if *tmpreg == 0 {
		*tmpreg = vreg.GetSSEReg()
	}
	data := NewAutoData(immfloat, 4)
	to.Datas.Add(data)
	(*ret) = append((*ret), NewSrcMemDestReg(*tmpreg, data.Name, opisfloat(movOrOp(i, op), true)))
	(*ret) = append((*ret), NewSrcMemDestReg(*tmpreg, varname, opisfloat(op, true)))
}

// OpTmpAndImmIntDestVarToAsm将形如 op<tmp,immint,var>或op<immint,tmp,var>的ir转换为asm
func OpTmpAndImmIntDestVarToAsm(varname, immint string, tmpreg *RegEnum, op ir.OPEnum, vreg *VReg, ret *[]TextNode) {
	if *tmpreg == 0 {
		*tmpreg = vreg.GetX86Reg(vrm64bit)
	}
	(*ret) = append((*ret), NewSrcNumDestReg(*tmpreg, immint, opisfloat(op, false)))
	(*ret) = append((*ret), NewSrcRegDestMem(varname, *tmpreg, opisfloat(ir.MOVOP, false)))
}

// OpTmpAndImmFloatDestVarToAsm将形如 op<tmp,immfloat,var>或op<immfloat,tmp,var>的ir转换为asm
func OpTmpAndImmFloatDestVarToAsm(to *AstToAsm, varname, immfloat string, tmpreg *RegEnum, op ir.OPEnum, vreg *VReg, ret *[]TextNode) {
	if *tmpreg == 0 {
		*tmpreg = vreg.GetSSEReg()
	}
	data := NewAutoData(immfloat, 4)
	to.Datas.Add(data)
	(*ret) = append((*ret), NewSrcMemDestReg(*tmpreg, data.Name, opisfloat(op, true)))
	(*ret) = append((*ret), NewSrcRegDestMem(varname, *tmpreg, opisfloat(ir.MOVOP, true)))
}

// OpTmpAndVarDestTmp将形如 op<tmp,var,tmp>或op<var,tmp,tmp>的ir转换为asm
func OpTmpAndVarDestTmp(varname string, tmpreg *RegEnum, op ir.OPEnum, sbt *ast.Sbt, vreg *VReg, ret *[]TextNode) {
	f := isfloatir(varname, tmpreg, sbt, vreg)
	(*ret) = append((*ret), NewSrcMemDestReg(*tmpreg, varname, opisfloat(op, f)))
}

// OpTmpAndImmIntDestTmp将形如 op<tmp,immint,tmp>或op<immint,tmp,tmp>的ir转换为asm
func OpTmpAndImmIntDestTmp(immint string, tmpreg *RegEnum, op ir.OPEnum, vreg *VReg, ret *[]TextNode) {
	if *tmpreg == 0 {
		*tmpreg = vreg.GetX86Reg(vrm64bit)
	}
	(*ret) = append((*ret), NewSrcNumDestReg(*tmpreg, immint, opisfloat(op, false)))
}

// OpTmpAndStackVarDestTmp将形如 op<tmp,stackvar,tmp>或op<immint,stackvar,tmo>的ir转换为asm
func OpTmpAndStackVarDestTmp(varname string, tmpreg *RegEnum, op ir.OPEnum, sbt *ast.Sbt, stack *Stack, vreg *VReg, ret *[]TextNode) {
	f := isfloatir(varname, tmpreg, sbt, vreg)
	offset := stack.HaveVar(varname).offset
	(*ret) = append((*ret), NewSrcRegOffsetDestReg(*tmpreg, RBP, -offset, opisfloat(op, f)))
}

// OpTmpAndImmIntDestStackVar将形如 op<tmp,immint,stackvar>或op<immint,tmp,stackvar>的ir转换为asm
func OpTmpAndImmIntDestStackVar(varname, immint string, tmpreg *RegEnum, op ir.OPEnum, stack *Stack, vreg *VReg, ret *[]TextNode) {
	if *tmpreg == 0 {
		*tmpreg = vreg.GetX86Reg(vrm64bit)
	}
	offset := stack.HaveVar(varname).offset
	(*ret) = append((*ret), NewSrcNumDestReg(*tmpreg, immint, opisfloat(op, false)))
	(*ret) = append((*ret), NewSrcRegDestRegOffset(RBP, *tmpreg, -offset, opisfloat(ir.MOVOP, false)))
}

// OpImmIntAndAndStackVarDestTmp将形如 op<immint,stackvar,tmp>或op<stackvar,immint,tmp>的ir转换为asm
func OpImmIntAndAndStackVarDestTmp(varname, immint string, tmpreg *RegEnum, op ir.OPEnum, sbt *ast.Sbt, stack *Stack, vreg *VReg, ret *[]TextNode, i int) {
	if *tmpreg == 0 {
		*tmpreg = vreg.GetX86Reg(vrm64bit)
	}
	offset := stack.HaveVar(varname).offset
	(*ret) = append((*ret), NewSrcRegOffsetDestReg(*tmpreg, RBP, -offset, opisfloat(movOrOp(i, op), false)))
	(*ret) = append((*ret), NewSrcNumDestReg(*tmpreg, immint, opisfloat(op, false)))
}

// OpVarAndVarDestTmp将形如 op<var,var,tmp>(src1和src2可以不相同)的ir转换为asm
func OpVarAndVarDestTmp(varname1, varname2 string, tmpreg *RegEnum, op ir.OPEnum, sbt *ast.Sbt, vreg *VReg, ret *[]TextNode, i int) {
	//语义检查保证src1和src2类型相同，正确的ir生成，保证两个源操作数与目的操作数，类型相同
	f := isfloatir(varname1, tmpreg, sbt, vreg)
	(*ret) = append((*ret), NewSrcMemDestReg(*tmpreg, varname1, opisfloat(movOrOp(i, op), f)))
	(*ret) = append((*ret), NewSrcMemDestReg(*tmpreg, varname2, opisfloat(op, f)))
}

// OpTmpAndVarDestVar将形如 op<var,tmp,var> 或op<tmp,var,var>的ir转换为asm
func OpTmpAndVarDestVar(varname1, varname2 string, tmpreg *RegEnum, op ir.OPEnum, sbt *ast.Sbt, vreg *VReg, ret *[]TextNode) {
	//语义检查保证src1和src2类型相同，正确的ir生成，保证两个源操作数与目的操作数，类型相同
	f := isfloatir(varname1, tmpreg, sbt, vreg)
	(*ret) = append((*ret), NewSrcMemDestReg(*tmpreg, varname1, opisfloat(op, f)))
	(*ret) = append((*ret), NewSrcRegDestMem(varname2, *tmpreg, opisfloat(ir.MOVOP, f)))
}

// OpStackVarAndStackVarDestTmp 将形如 op<stackvar,stackvar,tmp> 的ir转换为asm
func OpStackVarAndStackVarDestTmp(varname1, varname2 string, tmpreg *RegEnum, op ir.OPEnum, sbt *ast.Sbt, stack *Stack, vreg *VReg, ret *[]TextNode, i int) {
	//语义检查保证src1和src2类型相同，正确的ir生成，保证两个源操作数与目的操作数，类型相同
	f := isfloatir(varname1, tmpreg, sbt, vreg)
	offset1 := stack.HaveVar(varname1).offset
	offset2 := stack.HaveVar(varname2).offset
	(*ret) = append((*ret), NewSrcRegOffsetDestReg(*tmpreg, RBP, -offset1, opisfloat(movOrOp(i, op), f)))
	(*ret) = append((*ret), NewSrcRegOffsetDestReg(*tmpreg, RBP, -offset2, opisfloat(op, f)))
}

// OpTmpAndStackVarDestStackVar 将形如 op<tmp,stackvar,stackvar> 或op<stackvar,tmp,stackvar>的ir转换为asm (将两个源操作数和目的操作数不一样)
func OpTmpAndStackVarDestStackVar(src, dest string, tmpreg *RegEnum, op ir.OPEnum, sbt *ast.Sbt, stack *Stack, vreg *VReg, ret *[]TextNode) {
	//语义检查保证src1和src2类型相同，正确的ir生成，保证两个源操作数与目的操作数，类型相同
	f := isfloatir(src, tmpreg, sbt, vreg)
	offset1 := stack.HaveVar(src).offset
	offset2 := stack.HaveVar(dest).offset
	(*ret) = append((*ret), NewSrcRegOffsetDestReg(*tmpreg, RBP, -offset1, opisfloat(op, f)))
	(*ret) = append((*ret), NewSrcRegDestRegOffset(RBP, *tmpreg, -offset2, opisfloat(ir.MOVOP, f)))
}

// OpStackVarAndImmFloatDestTmp 将形如 op<stackvar,immfloat,tmp> 或op<immfloat,stackvar,tmp>的ir转换为asm
func OpStackVarAndImmFloatDestTmp(to *AstToAsm, varname, immfloat string, tmpreg *RegEnum, op ir.OPEnum, stack *Stack, vreg *VReg, ret *[]TextNode, i int) {
	if *tmpreg == 0 {
		*tmpreg = vreg.GetSSEReg()
	}
	data := NewAutoData(immfloat, 4)
	to.Datas.Add(data)
	offset := stack.HaveVar(varname).offset
	(*ret) = append((*ret), NewSrcMemDestReg(*tmpreg, data.Name, opisfloat(movOrOp(i, op), true)))
	(*ret) = append((*ret), NewSrcRegOffsetDestReg(*tmpreg, RBP, -offset, opisfloat(op, true)))
}

// OpTmpAndImmFloatDestTmp 将形如 op<tmp,immfloat,tmp> 或op<immfloat,tmp,tmp>的ir转换为asm
func OpTmpAndImmFloatDestTmp(to *AstToAsm, immfloat string, tmpreg *RegEnum, op ir.OPEnum, vreg *VReg, ret *[]TextNode) {
	data := NewAutoData(immfloat, 4)
	to.Datas.Add(data)
	(*ret) = append((*ret), NewSrcMemDestReg(*tmpreg, data.Name, opisfloat(op, true)))
}

// OpTmpAndImmFloatDestStackVar 将形如 op<tmp,immfloat,stackvar> 或op<immfloat,tmp,stackvar>的ir转换为asm
func OpTmpAndImmFloatDestStackVar(to *AstToAsm, varname, immfloat string, tmpreg *RegEnum, op ir.OPEnum, stack *Stack, vreg *VReg, ret *[]TextNode) {
	data := NewAutoData(immfloat, 4)
	to.Datas.Add(data)
	offset := stack.HaveVar(varname).offset
	(*ret) = append((*ret), NewSrcMemDestReg(*tmpreg, data.Name, opisfloat(op, true)))
	(*ret) = append((*ret), NewSrcRegDestRegOffset(RBP, *tmpreg, -offset, opisfloat(ir.MOVOP, true)))
}

func opisfloat(op ir.OPEnum, f bool) (Op TextKindEnum) {
	switch op {
	case ir.ADDOP:
		if f {
			Op = ADDSS
		} else {
			Op = ADD
		}
	case ir.SUBOP:
		if f {
			Op = SUBSS
		} else {
			Op = SUB
		}
	case ir.MOVOP:
		if f {
			Op = MOVSS
		} else {
			Op = MOV
		}
	default:
		panic(fmt.Errorf("未知的op : %s", op.String()))
	}
	return
}

func isfloatir(dest string, tmpreg *RegEnum, sbt *ast.Sbt, vreg *VReg) bool {
	f := false
	info := sbt.Have(dest)
	switch info.Type() {
	case "int":
	case "float":
		f = true
	default:
		panic(fmt.Errorf("未知的类型：%s", info.Type()))
	}
	if *tmpreg == 0 {
		if f {
			*tmpreg = vreg.GetSSEReg()
		} else {
			*tmpreg = vreg.GetX86Reg(vrm64bit)
		}
	}
	return f
}

func retvreg(name string, sbt *ast.Sbt, vreg *VReg) RegEnum {
	info := sbt.Have(name)
	switch info.Type() {
	case "int":
		return vreg.GetX86Reg(vrm64bit)
	case "float":
		return vreg.GetSSEReg()
	}
	panic(fmt.Errorf("不支持的全局变量类型 name=%s   type=%s", name, info.Type()))
}

func movOrOp(i int, op ir.OPEnum) ir.OPEnum {
	if i == 0 {
		return ir.MOVOP
	}
	return op
}
